﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

#region Import AForge Namespace
using AForge;
using AForge.Video;
using AForge.Video.DirectShow;
#endregion

namespace EyesOnTrain
{
    class CaptureEngine
    {
        private static Lazy<CaptureEngine> _instance = new Lazy<CaptureEngine>(() => new CaptureEngine());
        public static CaptureEngine Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private IList<VideoSourceInfo> _sourceInfos = new List<VideoSourceInfo>();

        public event EventHandler<NewFrameCapturedEventArgs> NewFrameEvent;

        private CaptureEngine()
        {
            InitEngine();
        }

        private void InitEngine()
        {
            InitVideoSourceList();
        }

        private void InitVideoSourceList()
        {
            _sourceInfos.Clear();

            var videoInputDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            for (int i = 0; i < videoInputDevices.Count; i++)
            {
                var id = videoInputDevices[i].MonikerString;
                var videoSource = new VideoCaptureDevice(id);
                var asyncVideoSource = new AsyncVideoSource(videoSource);
                asyncVideoSource.NewFrame += asyncVideoSource_NewFrame;
                _sourceInfos.Add(new VideoSourceInfo(id, asyncVideoSource));
            }
        }

        void asyncVideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            var videoSource = sender as IVideoSource;
            if (videoSource == null)
                throw new InvalidCastException();

            var item = _sourceInfos.FirstOrDefault(p => p.Source == videoSource);
            if (string.IsNullOrEmpty(item.ID))
                throw new ArgumentException();

            OnNewFrameCapturedFromVideoSource(item, eventArgs);
        }

        private Task CreateStartTask(List<VideoSourceInfo> sourceInfoList)
        {
            if (sourceInfoList == null)
                throw new ArgumentNullException("sourceInfoList");

            var task = new Task(() =>
            {
                sourceInfoList.ForEach(p =>
                    {
                        p.Source.Start();
                        Thread.Sleep(1000);
                        Trace.TraceInformation("Start video device at: " + p.Source.Source);
                    });
            });
            task.Start();

            return task;
        }

        public async void Start()
        {
            Trace.TraceInformation("Capture Engine Start");
            var videoList = _sourceInfos.ToList();
            await CreateStartTask(videoList);
        }

        public void SignalToStop()
        {
            _sourceInfos.ToList().ForEach(p => p.Source.SignalToStop());
        }

        public void Stop()
        {
            _sourceInfos.ToList().ForEach(p => p.Source.Stop());
        }

        public string[] DeviceIds
        {
            get
            {
                return _sourceInfos.Select(p => p.ID).ToArray();
            }
        }

        private void OnNewFrameCapturedFromVideoSource(VideoSourceInfo videoSource, NewFrameEventArgs eventArgs)
        {
            var handler = NewFrameEvent;
            if (handler != null)
            {
                NewFrameEvent(this, new NewFrameCapturedEventArgs(videoSource.ID, eventArgs.Frame));
            }
        }
    }

    struct VideoSourceInfo
    {
        private string _id;
        private IVideoSource _videoSource;

        public string ID
        {
            get { return _id; }
        }

        public IVideoSource Source
        {
            get { return _videoSource; }
        }

        public VideoSourceInfo(string id, IVideoSource source)
        {
            _id = id;
            _videoSource = source;
        }
    }
}
